package drds.plus.executor.function.scalar.subquery;

import drds.plus.executor.ExecuteContext;
import drds.plus.executor.ExecutorException;
import drds.plus.executor.function.scalar.ScalarFunction;
import drds.plus.executor.row_values.RowValues;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.FunctionType;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.optimizer.OptimizerContext;
import drds.plus.sql_process.type.Type;
import drds.plus.sql_process.utils.OptimizerUtils;
import drds.tools.$;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractSubQueryFunction extends ScalarFunction {
    public FunctionType getFunctionType() {
        return FunctionType.scalar;
    }

    public Type getReturnType() {
        List argList = this.function.getArgList();
        if (!$.isNotNullAndHasElement(argList)) {
            throw new IllegalArgumentException("必须含有参数");
        }
        List<Item> itemList = null;
        if (argList.get(0) instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) {
            itemList = ((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) argList.get(0)).getItemList();
        } else if (argList.get(0) instanceof Query) {
            itemList = ((Query) argList.get(0)).getSelectItemList();
        } else {
            throw new ExecutorException("subQuery is not Query or Query");
        }
        if (itemList.size() != 1) {
            throw new IllegalArgumentException("only one column can be in sub setWhereAndSetNeedBuild, sub setWhereAndSetNeedBuild is: " + this.getQuery());
        }
        Item item = itemList.get(0);
        return item.getType();
    }

    public Object scalarCalucate(ExecuteContext executeContext, RowValues rowData) {
        drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query query = this.getQuery();
        /**
         * 原理是外部查询的结果(非完整执行,还剩下关联子查询待进行处理)替换关联子查询的变量,然后生成关联子查询执行计划并执行。返回结果和外部查询进行逻辑计算得到真正的值。
         */
        if (query.isCorrelatedSubQuery()) {
            Query queryNode = OptimizerUtils.convertExecutePlanToNode(query);
            // 获取correlated column
            List<Item> correlatedSubQueryItemList = queryNode.getCorrelatedSubQueryItemList();
            Map<Long, Object> correlateSubQueryItemIdToValueMap = new HashMap<Long, Object>();
            // 从rowData中找到对应的column进行替换
            for (Item item : correlatedSubQueryItemList) {
                Object value = getValue(rowData, item);
                correlateSubQueryItemIdToValueMap.put(item.getCorrelateSubQueryItemId(), value);
            }
            // 替换correlated column value
            OptimizerContext.getOptimizerContext().getOptimizer().subQueryAssignValueAndReturnNextSubQueryFunction(queryNode, correlateSubQueryItemIdToValueMap, executeContext.getExtraCmds());
            // 重新生成执行计划
            query = (drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) OptimizerContext.getOptimizerContext().getOptimizer().optimizeNodeAndToExecutePlanAndOptimizeExecutePlan(queryNode, executeContext.getParameters(), executeContext.getExtraCmds());
        }
        return this.compute(executeContext, new Object[]{query});

    }

    private drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query getQuery() {
        return this.getQuery(this.function.getArgList().toArray());
    }

    protected drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query getQuery(Object[] args) {
        if (args == null || args.length != 1) {
            throw new ExecutorException("impossible");
        }
        if (args[0] instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) {
            return (drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) args[0];
        } else {
            throw new ExecutorException("sub query is not Query");
        }
    }

    public Object getValue(RowValues rowData, Item item) {
        if (rowData == null) {
            return null;
        }
        Integer index = rowData.getParentCursorMetaData().getIndex(item.getTableName(), item.getColumnName(), item.getAlias());
        if (index == null) {
            throw new ExecutorException("subquery correlated column:" + item + "is not found in row:" + rowData);
        }
        Object object = rowData.getObject(index);
        return object;
    }
}
